home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / scope / 051-075 / scopedisk57 / expand / main.c < prev    next >
C/C++ Source or Header  |  1995-03-19  |  8KB  |  252 lines

  1. /*
  2.  *                              M A I N . C
  3.  *
  4.  *  A file maintenance utility to decompress files; compatible with the
  5.  *  public domain COMPRESS v4.0 utility by Spencer W. Thomas, et al.
  6.  *
  7.  *  Algorithm from "A Technique for High Performance Data Compression",
  8.  *  Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19.
  9.  *
  10.  *  The impetus for this program comes from a desire to decompress files
  11.  *  in an MS-DOS environment with 16-bit codes using fewer machine
  12.  *  resources than the public domain COMPRESS utility.  Memory usage
  13.  *  for MS-DOS is less than 250 kbytes for 16-bit codes.
  14.  *
  15.  *  The code uses many ANSI-C style contructs with no apologies.
  16.  *-----------------------------------------------------------------------
  17.  *  Authors: Lyle V. Rains
  18.  *      based on the source of COMPRESS v4.0 by Spencer Thomas, et al.
  19.  *-----------------------------------------------------------------------
  20.  *  Revision History:
  21.  */
  22.  
  23. #define MAIN
  24. #include "main.h"
  25.  
  26. char *myname                  = MYNAME;
  27. static char *stdinname        = STDIN;
  28. static char *stdoutname       = STDOUT;
  29.  
  30. /* Magic 2-byte file header */
  31. static char header[]          = {0x1F, 0x9D};
  32.  
  33. static char xbuf[XBUFSIZE];                /* Big buffers for setvbuf()*/
  34. static char zbuf[ZBUFSIZE];
  35.  
  36.  
  37. static void usage(all)
  38.   int all;
  39. {
  40.   U("  Usage:  %s [%ch(elp)] [%ccko] [%cf] [files ...]\n", myname, SW, SW, SW);
  41.  
  42.   if (!all) return;
  43.  
  44. #ifdef __DATE__
  45.   U("\n  Version %s, %s\n", VERSION, __DATE__);
  46. #else
  47.   U("\n  Version %s\n", VERSION);
  48. #endif
  49.   U("  Decompresses files in a method compatible with COMPRESS V4.0\n");
  50.   U("  INITIAL PUBLIC RELEASE.  Please send feedback and bug reports to:\n");
  51.   U("      Lyle Rains, Compuserve User ID 71250,324\n");
  52.   U("\n  Switches:\n");
  53.   U("     %ch        this help message.\n", SW);
  54.   U("     %cc        concatenate output to stdout.\n", SW);
  55. #if KILL == YES
  56.   U("     %ck        keep input file (default: delete).\n", SW);
  57. #else
  58.   U("     %ck        kill input file (default: keep).\n", SW);
  59. #endif
  60.   U("     %co        overwrite pre-existing output file.\n", SW);
  61.   U("     %cf        filter (%s stdin to stdout; ignore additional args).\n", SW, myname);
  62. #if DEBUGOUT
  63.   U("     %cd        print debug info to stderr.\n", SW);
  64. #endif
  65.   U("  File arguments:\n");
  66.   U("     infile    %s infile (e.g., FOO.C%s) to default outfile (FOO.C)\n", myname, ZSUFFIX);
  67.   U("     outfile   %s to outfile (e.g., FOO.C) from default infile (FOO.C%s)\n", myname, ZSUFFIX);
  68.   U("     in%cout    %s infile to outfile (overrides %cc and defaults).\n", TO, myname, SW);
  69.  
  70.   return;
  71. }
  72.  
  73.  
  74. static int do_expand();
  75.  
  76.  
  77. main(argc, argv)
  78.   int argc;
  79.   char *argv[];
  80. {
  81.   char **nextarg;
  82.   int argsleft;
  83.   char *arg, *out;
  84.   int kill, overwrite, cat;
  85.   char name_tmp[NAMEBUFSIZ];
  86.   FILE *infile, *outfile;
  87.   char userinput[2];
  88.  
  89.   if ((argsleft = argc) <= 1) {
  90. #   if FILTER == YES
  91.       do_expand(stdin, stdout, stdinname, stdoutname); exit (NORMAL);
  92. #   else
  93.       /* No arguments -- give short usage message and exit with error */
  94.       usage(QUICK); exit (ERROR);
  95. #   endif
  96.   }
  97.  
  98.   kill = KILL;
  99.   cat = NO;
  100.   overwrite = NO;
  101.  
  102.   /* Process command line arguments */
  103.   nextarg = argv;
  104.   while (--argsleft > 0) {
  105.     if (*(arg = *++nextarg) == SW) {
  106.       /* Process command switches */
  107.       while (*++arg) {
  108.         switch (*arg) {
  109.         case 'h':
  110.         case 'H':
  111.         case '?':
  112.           usage(ALL); exit (NORMAL);
  113.         case 'f':
  114.         case 'F':
  115.           do_expand(stdin, stdout, stdinname, stdoutname); exit (NORMAL);
  116.         case 'c':
  117.         case 'C':
  118.           cat = !cat; break;
  119.         case 'o':
  120.         case 'O':
  121.           overwrite = !overwrite; break;
  122.         case 'k':
  123.         case 'K':
  124.           kill = !kill; break;
  125.         default:
  126.           eprintf("%s: bad switch '-%c'.\n", myname, *arg);
  127.           usage(QUICK);
  128.           exit (ERROR);
  129. #       if DEBUGOUT
  130.           case 'd':
  131.           case 'D':
  132.             debug = !debug; break;
  133. #       endif
  134.         }
  135.       }
  136.     }
  137.     else if (*arg == TO) {
  138.       /* Inappropriate place to name output file.  Find file name. */
  139.       if (!*++arg) arg = --argsleft ? *++nextarg : "<noname>";
  140.       errexit(("%s: no input file for output '=%s'.\n", myname, arg), ERROR);
  141.     }
  142.     else {
  143.       /* Get input/output filespecs and open them for processing.
  144.        * Note:  filename processing is necessarily system dependent.
  145.        * I am providing suitable routines for MS-DOS, but you must
  146.        * supply your own routines as necessary for other systems.
  147.        *
  148.        * First check to see if there is an output file spec. If the
  149.        * output prefix character isn't in the current argument, check
  150.        * the beginning of the next arg, in case the user added whitespace.
  151.        */
  152.       if (  (out = strchr(arg, TO)) != 0
  153.          || (  (argsleft > 1)
  154.             && (out = (**(nextarg + 1) == TO) ? (--argsleft, *++nextarg) : NULL) != 0
  155.             )
  156.          )
  157.       {
  158.         /* We found the prefix character.  Now find the output
  159.          * filespec, again allowing for the possible inclusion of
  160.          * whitespace after the prefix character.
  161.          */
  162.         *out = '\0';
  163.         if ( !*++out && (out = --argsleft ? *++nextarg : NULL) == 0 ) {
  164.           errexit(("%s: missing output filespec for '%s='.\n", myname, arg),ERROR);
  165.         }
  166.       }
  167.       else if (cat) out = stdoutname;
  168.       else {
  169.         /* Create a default output filespec, trying the following:
  170.          *  1. If the input filespec file extension ends with ZSUFFIX,
  171.          *     then default output spec is the same as the input, with
  172.          *     ZSUFFIX removed.
  173.          *  2. If the input doesn't end in ZSUFFIX, assume that the
  174.          *     user is really giving the desired output spec, and try to
  175.          *     create a default input spec by appending ZSUFFIX to
  176.          *     the filespec and seeing if any such input file exists.
  177.          *  3. Give up (we really tried!), print an error and continue.
  178.          */
  179.         out = strcpy(name_tmp, arg);
  180.         if (!to_xname(out)) {
  181.           out = arg;
  182.           arg = name_tmp;
  183.           to_zname(arg);
  184.         }
  185.       }
  186.       if (!overwrite && out != stdoutname && (outfile = fopen(out, "r")) != 0) {
  187.         eprintf("%s: output '%s' exists.  Overwrite? ", myname, out);
  188.         if (fgets(userinput, 2, stdin) && tolower(*userinput) == 'y')
  189.           fclose(outfile);
  190.         else
  191.           exit (ERROR);
  192.       }
  193.       if ((outfile = (out == stdoutname) ? (FILE *)stdout : fopen(out, "w")) == 0) {
  194.         errexit(("%s: can't open output '%s'.\n", myname, out), ERROR);
  195.       }
  196.       if ((infile = fopen(arg, "r")) == 0) {
  197.         errexit(("%s: can't open input '%s'.\n", myname, arg), ERROR);
  198.       }
  199.       eprintf("expanding '%s' to '%s'.\n", arg, out);
  200.       DBG(("\t overwrite = %d, cat = %d, kill = %d\n", overwrite, cat, kill));
  201.       do_expand(infile, outfile, arg, out);
  202.       if (kill) unlink(arg);
  203.     }
  204.   }
  205. }
  206.  
  207. static int do_expand(in, out, inname, outname)
  208.   FILE *in;
  209.   FILE *out;
  210.   char *inname;
  211.   char *outname;
  212. {
  213.   int maxbits;
  214.   int clearmode;
  215.  
  216.   setvbuf(in, zbuf, _IOFBF, ZBUFSIZE);
  217.   setvbuf(out, xbuf, _IOFBF, XBUFSIZE);
  218.   setbinary(in);                        /* Some systems require this    */
  219.   setbinary(out);
  220.   /* Check for the magic number header */
  221.   if ((char)getc(in) != header[0] || (char)getc(in) != header[1]) {
  222.     errexit(("%s: '%s' not in compressed format.\n", myname, inname), ERROR);
  223.   }
  224.   maxbits = getc(in);
  225.   clearmode = maxbits & BLOCK_MASK;
  226.   if ((maxbits &= BIT_MASK) > Maxbits) {
  227.     errexit(("%s: '%s' uses %d-bit compression (%s allows %d-bits maximum).\n",
  228.             myname, inname, maxbits, myname, Maxbits), ERROR);
  229.   }
  230.   switch (expand(in, out, maxbits, clearmode)) {
  231.   case OK:
  232.     fclose(in);
  233.     if (out != stdout) fclose(out);
  234.     else fflush(out);
  235.     return (OK);
  236.   case NOMEM:
  237.     errexit(("%s: not enough memory to expand '%s'.\n", myname, inname), ERROR);
  238.   case TOKTOOBIG:
  239.     errexit(("%s: token too long in '%s'.\n", myname, inname), ERROR);
  240.   case READERR:
  241.     errexit(("%s: read error on input '%s'.\n", myname, inname), ERROR);
  242.   case WRITEERR:
  243.     errexit(("%s: write error on output '%s'.\n", myname, outname), ERROR);
  244.   case CODEBAD:
  245.     errexit(("%s: illegal code found in '%s'.\n", myname, inname), ERROR);
  246.   case TABLEBAD:
  247.     errexit(("%s: internal error -- tables corrupted.\n", myname), ERROR);
  248.   default:
  249.     errexit(("%s: internal error -- illegal return value.\n", myname), ERROR);
  250.   }
  251. }  
  252.